package de.ret_world.openrpgstudio.eventengine;

import buoy.event.CellValueChangedEvent;
import buoy.event.CommandEvent;
import buoy.event.SelectionChangedEvent;
import buoy.event.ValueChangedEvent;
import buoy.widget.*;
import de.ret_world.openrpgstudio.editor.Database;
import de.ret_world.openrpgstudio.editor.Main;
import de.ret_world.openrpgstudio.editor.Project;
import de.ret_world.openrpgstudio.editor.gui.ScriptArea;
import de.ret_world.openrpgstudio.editor.helpers.Internationalization;
import de.ret_world.openrpgstudio.editor.helpers.ScriptTools;
import de.ret_world.openrpgstudio.editor.plugininterfaces.DatabasePlugin;
import de.ret_world.openrpgstudio.editor.script.ScriptObject;
import de.ret_world.openrpgstudio.eventengine.scripts.Event;
import java.awt.Insets;
import java.awt.Rectangle;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.lib.LibFunction;
import org.luaj.vm2.lib.jse.JsePlatform;

/**
 *
 * @author Emu
 */
public class EventEditor extends DatabasePlugin {

    /**
     * This plug-in ID.
     */
    public static final String PLUGIN_ID = "de.ret_world.openrpgstudio.eventengine";
    //Variables
    private BSplitPane split;
    private BList list;
    private HashMap<Event, String> updateMap;
    private HashMap<Event, String> actionMap;
    private BTabbedPane tabbed;
    private ScriptArea script;
    private ScriptArea update;
    private ScriptArea action;
    private BTextField txtLocation;
    private BButton btnLocation;
    private BButton btnSkin;
    private BTextArea txtANotes;
    private BTable tblTags;
    private BButton btnAddTag;
    private BButton btnEditTag;
    private BButton btnRemoveTag;

    @Override
    public void init(BSplitPane container) {
        Internationalization intn = Internationalization.getInstance();

        HashMap<String, ScriptObject> scriptMap = Project.getInstance().getDatabase().getScriptMap(Event.TYPE);
        Collection<ScriptObject> events;
        if (scriptMap == null) {
            events = new ArrayList<>();
        } else {
            events = scriptMap.values();
        }
        //Get Update and action scripts
        updateMap = new HashMap<>();
        actionMap = new HashMap<>();
        for (ScriptObject o : events) {
            Event e = (Event) o;
            Path scriptFile = Project.getInstance().getFolder("scripts").resolve("event_" + e.getID() + ".lua");
            String escript = ScriptTools.getInstance().readScriptFile(scriptFile);

            String[] lines = escript.split("\\\n");
            int ubegin = 0, uend = 0, abegin = 0, aend = 0, tmp = 0;
            for (int i = 0; i < lines.length; i++) {
                if (lines[i].startsWith("event.update = ")) {
                    ubegin = i + 1;
                } else if (lines[i].startsWith("end")) {
                    tmp = i;
                    aend = i - 1;
                } else if (tmp == i - 1 && lines[i].startsWith("event.action =")) {
                    uend = tmp - 1;
                    abegin = i + 1;
                }
            }

            String uscript = "";
            for (int i = ubegin; i <= uend; i++) {
                uscript += lines[i] + "\n";
            }
            uscript = uscript.trim();

            String ascript = "";
            for (int i = abegin; i <= aend; i++) {
                ascript += lines[i] + "\n";
            }
            ascript = ascript.trim();

            updateMap.put(e, uscript);
            actionMap.put(e, ascript);
        }

        //GUI
        LayoutInfo li = new LayoutInfo(LayoutInfo.WEST, LayoutInfo.HORIZONTAL, new Insets(5, 5, 5, 5), null);

        split = container;
        split.setDividerLocation(Integer.parseInt(config.getProperty("gui.divider", "175")));

        //Left side
        BorderContainer left = new BorderContainer();
        left.add(new BLabel(intn.getString("EE.EVENTS")), BorderContainer.NORTH, li);
        list = new BList(events);
        BScrollPane scrl = new BScrollPane(BScrollPane.SCROLLBAR_NEVER, BScrollPane.SCROLLBAR_AS_NEEDED);
        scrl.setContent(list);
        scrl.setForceHeight(true);
        list.addEventLink(SelectionChangedEvent.class, this, "selectionChanged");
        left.add(BOutline.createEtchedBorder(scrl, false), BorderContainer.CENTER, new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.BOTH, new Insets(5, 5, 5, 5), null));

        RowContainer rc = new RowContainer();
        BButton btn = new BButton(new ImageIcon(manager.getPluginClassLoader(manager.getRegistry().getPluginDescriptor(Main.PLUGIN_ID)).getResource("plus.png")));
        btn.addEventLink(CommandEvent.class, this, "addEvent");
        rc.add(btn, li);
        btn = new BButton(new ImageIcon(manager.getPluginClassLoader(manager.getRegistry().getPluginDescriptor(Main.PLUGIN_ID)).getResource("wrench.png")));
        btn.addEventLink(CommandEvent.class, this, "editEvent");
        rc.add(btn, li);
        btn = new BButton(new ImageIcon(manager.getPluginClassLoader(manager.getRegistry().getPluginDescriptor(Main.PLUGIN_ID)).getResource("minus.png")));
        btn.addEventLink(CommandEvent.class, this, "removeEvent");
        rc.add(btn, li);
        left.add(rc, BorderContainer.SOUTH, li);

        split.add(left, 0);

        //Right side
        tabbed = new BTabbedPane();
        tabbed.addEventLink(SelectionChangedEvent.class, this, "tabChanged");

        FormContainer right = new FormContainer(new double[]{0, 1}, new double[]{1});
        ColumnContainer cc = new ColumnContainer();

        cc.add(new BLabel(intn.getString("EE.LOCATION")), li);
        txtLocation = new BTextField("");
        txtLocation.setEditable(false);
        cc.add(txtLocation, li);
        btnLocation = new BButton(intn.getString("EE.PICKLOCATION"));
        btnLocation.addEventLink(CommandEvent.class, this, "pickLocation");
        cc.add(btnLocation, li);

        cc.add(new BLabel(intn.getString("EE.SKIN")), li);
        btnSkin = new BButton("SKIN");
        btnSkin.setEnabled(false);
        cc.add(btnSkin, li);

        cc.add(new BLabel(intn.getString("EE.NOTES")), li);
        txtANotes = new BTextArea(10, 10);
        txtANotes.setWrapStyle(BTextArea.WRAP_WORD);
        txtANotes.addEventLink(ValueChangedEvent.class, this, "notesChanged");
        cc.add(new BScrollPane(txtANotes, BScrollPane.SCROLLBAR_NEVER, BScrollPane.SCROLLBAR_AS_NEEDED), li);

        BorderContainer bc = new BorderContainer();
        bc.add(new BLabel(intn.getString("EE.TAGS")), BorderContainer.NORTH, li);
        tblTags = new BTable(0, 2);
        tblTags.setColumnHeader(0, intn.getString("EE.KEY"));
        tblTags.setColumnHeader(1, intn.getString("EE.VALUE"));
        tblTags.setColumnEditable(0, false);
        tblTags.setColumnEditable(1, true);
        tblTags.setSelectionMode(BTable.SELECT_CELLS);
        tblTags.setMultipleSelectionEnabled(false);
        tblTags.addEventLink(CellValueChangedEvent.class, this, "tableChanged");
        bc.add(new BScrollPane(tblTags, BScrollPane.SCROLLBAR_NEVER, BScrollPane.SCROLLBAR_AS_NEEDED), BorderContainer.CENTER, new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.BOTH, new Insets(5, 5, 5, 5), null));

        rc = new RowContainer();
        btnAddTag = new BButton(new ImageIcon(manager.getPluginClassLoader(manager.getRegistry().getPluginDescriptor(Main.PLUGIN_ID)).getResource("plus.png")));
        btnAddTag.addEventLink(CommandEvent.class, this, "addTag");
        rc.add(btnAddTag, li);
        btnEditTag = new BButton(new ImageIcon(manager.getPluginClassLoader(manager.getRegistry().getPluginDescriptor(Main.PLUGIN_ID)).getResource("wrench.png")));
        btnEditTag.addEventLink(CommandEvent.class, this, "editTag");
        rc.add(btnEditTag, li);
        btnRemoveTag = new BButton(new ImageIcon(manager.getPluginClassLoader(manager.getRegistry().getPluginDescriptor(Main.PLUGIN_ID)).getResource("minus.png")));
        btnRemoveTag.addEventLink(CommandEvent.class, this, "removeTag");
        rc.add(btnRemoveTag, li);
        bc.add(rc, BorderContainer.SOUTH, li);

        ExplicitContainer ec = new ExplicitContainer();
        ec.add(bc, new Rectangle(200, 270));
        cc.add(ec, new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.BOTH, new Insets(5, 5, 5, 5), null));

        right.add(cc, 0, 0, new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.BOTH, new Insets(5, 5, 5, 5), null));

        FormContainer cscripts = new FormContainer(new double[]{1}, new double[]{0, 1, 0, 1});

        cscripts.add(new BLabel(intn.getString("EE.UPDATESCRIPT")), 0, 0, li);
        update = new ScriptArea();
        cscripts.add(update, 0, 1, new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.BOTH, new Insets(5, 5, 5, 5), null));
        cscripts.add(new BLabel(intn.getString("EE.ACTIONSCRIPT")), 0, 2, li);
        action = new ScriptArea();
        cscripts.add(action, 0, 3, new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.BOTH, new Insets(5, 5, 5, 5), null));

        right.add(cscripts, 1, 0, new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.BOTH, new Insets(5, 5, 5, 5), null));

        tabbed.add(right, intn.getString("GUI"));

        script = new ScriptArea();
        tabbed.add(script, intn.getString("SCRIPT"));

        split.add(tabbed, 1);

        enableAll(false);
    }

    private void enableAll(boolean en) {
        script.setEnabled(en);
        txtLocation.setEnabled(en);
        btnLocation.setEnabled(en);
        txtANotes.setEnabled(en);
        update.setEnabled(en);
        action.setEnabled(en);
        tblTags.setEnabled(en);
        btnAddTag.setEnabled(en);
        btnEditTag.setEnabled(en);
        btnRemoveTag.setEnabled(en);
    }

    private void refreshGUI(Event e) {
        script.setText(e.toScript());
        txtLocation.setText(e.getLocation().toString());
        txtANotes.setText(e.getNotes());
        tblTags.removeAllRows();
        for (String k : e.getTags().keySet()) {
            tblTags.addRow(new String[]{k, e.getTags().get(k)});
        }
        tblTags.repaint();
        update.setText(updateMap.get(e));
        action.setText(actionMap.get(e));
    }

    @Override
    public void save() {
        config.setProperty("gui.divider", "" + split.getDividerLocation());
        config.storeConfig();

        if (list.getSelectedIndex() > -1) {
            Event e = (Event) list.getSelectedValue();
            if (tabbed.getSelectedTab() == 1) {
                LuaTable env = JsePlatform.standardGlobals();
                LuaThread.setGlobals(env);
                HashMap<String, LibFunction> libraries = Project.getInstance().getScriptRegister().getLibraries();

                for (LibFunction lib : libraries.values()) {
                    env.load(lib);
                }

                ScriptTools.getInstance().runScriptString(script.getText(), env);

                if (!env.get(Event.TYPE).isnil()) {
                    e.create(env.get("event"));
                }
            }
        }

        generateScriptFile();
    }

    private void selectionChanged() {
        if (list.getSelectedIndex() > -1) {
            enableAll(true);
            Event e = (Event) list.getSelectedValue();
            refreshGUI(e);
        } else {
            enableAll(false);
        }
    }

    private void addEvent() {
        Project project = Project.getInstance();
        Internationalization intn = Internationalization.getInstance();
        Database database = Project.getInstance().getDatabase();
        String id = JOptionPane.showInputDialog(split.getComponent(), intn.getString("EE.SETID"));
        if (id != null && database.getScriptObject(Event.TYPE, id) == null) {
            Event e = new Event();
            e.setID(id);
            database.putScriptObject(Event.TYPE, id, e);

            list.add(e);
            updateMap.put(e, "-- update is called every few miliseconds");
            actionMap.put(e, "-- action is called by the action() function");

            if (!project.getScriptRegister().isScriptRegistered("event_" + e.getID())) {
                project.getScriptRegister().registerScript("event_" + e.getID(), Paths.get("event_" + e.getID() + ".lua"), 0);
                ScriptTools.getInstance().saveScriptFile(project.getFolder("scripts").resolve("event_" + e.getID() + ".lua"), "");
            }
        }
    }

    private void editEvent() {
        Internationalization intn = Internationalization.getInstance();
        Database database = Project.getInstance().getDatabase();
        Project project = Project.getInstance();
        if (list.getSelectedIndex() > -1) {
            Event e = (Event) list.getSelectedValue();
            String oldID = e.getID();
            String id = JOptionPane.showInputDialog(split.getComponent(), intn.getString("EE.NEWID"));
            if (id != null) {
                e.setID(id);
                if (!oldID.equals(e.getID())) {
                    database.removeScriptObject(Event.TYPE, oldID);
                    database.putScriptObject(Event.TYPE, e.getID(), e);

                    Path oldScript = project.getScriptRegister().getAbsolutePath("event_" + oldID);
                    project.getScriptRegister().unregisterScript("event_" + oldID);

                    try {
                        Files.move(oldScript, project.getFolder("scripts").resolve("event_" + e.getID() + ".lua"));
                    } catch (IOException ex) {
                        log.debug(ex);
                    }

                    project.getScriptRegister().registerScript("event_" + e.getID(), Paths.get("event_" + e.getID() + ".lua"), -9);
                    list.repaint();
                }
            }
        }
    }

    private void removeEvent() {
        Internationalization intn = Internationalization.getInstance();
        if (list.getSelectedIndex() > -1) {
            Event e = (Event) list.getSelectedValue();
            int r = JOptionPane.showConfirmDialog(split.getComponent(), intn.getString("EE.REMOVEE"), null, JOptionPane.YES_NO_OPTION);
            if (r == JOptionPane.YES_OPTION) {
                Project.getInstance().getDatabase().removeScriptObject(Event.TYPE, e.getID());
                try {
                    Files.deleteIfExists(Project.getInstance().getScriptRegister().getAbsolutePath("event_" + e.getID()));
                } catch (IOException ex) {
                    log.debug(ex);
                }
                Project.getInstance().getScriptRegister().unregisterScript("event_" + e.getID());

                list.remove(list.getSelectedIndex());
                updateMap.remove(e);
                actionMap.remove(e);
                list.clearSelection();
            }
        }
    }

    private void tabChanged() {
        if (list.getSelectedIndex() > -1) {
            Event e = (Event) list.getSelectedValue();
            if (tabbed.getSelectedTab() == 0) {
                LuaTable env = JsePlatform.standardGlobals();
                LuaThread.setGlobals(env);
                HashMap<String, LibFunction> libraries = Project.getInstance().getScriptRegister().getLibraries();

                for (LibFunction lib : libraries.values()) {
                    env.load(lib);
                }

                ScriptTools.getInstance().runScriptString(script.getText(), env);

                //Get Update and action script
                String[] lines = script.getText().split("\\\n");
                int ubegin = 0, uend = 0, abegin = 0, aend = 0, tmp = 0;
                for (int i = 0; i < lines.length; i++) {
                    if (lines[i].startsWith("event.update = ")) {
                        ubegin = i + 1;
                    } else if (lines[i].startsWith("end")) {
                        tmp = i;
                        aend = i - 1;
                    } else if (tmp == i - 1 && lines[i].startsWith("event.action =")) {
                        uend = tmp - 1;
                        abegin = i + 1;
                    }
                }

                String uscript = "";
                for (int i = ubegin; i <= uend; i++) {
                    uscript += lines[i] + "\n";
                }
                uscript = uscript.trim();

                String ascript = "";
                for (int i = abegin; i <= aend; i++) {
                    ascript += lines[i] + "\n";
                }
                ascript = ascript.trim();

                updateMap.put(e, uscript);
                actionMap.put(e, ascript);

                if (!env.get("event").isnil()) {
                    e.create(env.get("event"));
                    refreshGUI(e);
                }

            } else {
                e.setNotes(txtANotes.getText());
                HashMap<String, String> tags = e.getTags();
                for (int i = 0; i < tblTags.getRowCount(); i++) {
                    tags.put("" + tblTags.getCellValue(i, 0), "" + tblTags.getCellValue(i, 1));
                }

                updateMap.put(e, update.getText());
                actionMap.put(e, action.getText());

                String scrpt = "-- OpenRPGStudio Event Script --\n"
                        + "-- Script is generated by application! Change with care! --\n\n"
                        + e.toScript() + "\n"
                        + "event.update = function()\n"
                        + updateMap.get(e) + "\n"
                        + "end\n"
                        + "event.action = function (caller)\n"
                        + actionMap.get(e) + "\n"
                        + "end\n\n";

                script.setText(scrpt);
            }
        }
    }

    private void pickLocation() {
    }

    private void notesChanged() {
        ((Event) list.getSelectedValue()).setNotes(txtANotes.getText());
    }

    private void addTag() {
        Internationalization intn = Internationalization.getInstance();
        String id = JOptionPane.showInputDialog(split.getComponent(), intn.getString("EE.NEWTAG"));
        if (id != null) {
            tblTags.addRow(new String[]{id, ""});
            tblTags.repaint();
            tblTags.dispatchEvent(new CellValueChangedEvent(tblTags, tblTags.getRowCount() - 1, 0));
        }
    }

    private void editTag() {
        Internationalization intn = Internationalization.getInstance();
        if (tblTags.getSelectedRows() != null) {
            String id = JOptionPane.showInputDialog(split.getComponent(), intn.getString("EE.EDITTAG"));
            if (id != null) {
                tblTags.setCellValue(tblTags.getSelectedRows()[0], 0, id);
                tblTags.repaint();
                tblTags.dispatchEvent(new CellValueChangedEvent(tblTags, tblTags.getSelectedRows()[0], 0));
            }
        }
    }

    private void removeTag() {
        Internationalization intn = Internationalization.getInstance();
        if (tblTags.getSelectedRows() != null) {
            int r = JOptionPane.showConfirmDialog(split.getComponent(), intn.getString("EE.REMOVETAG"), null, JOptionPane.YES_NO_OPTION);
            if (r == JOptionPane.YES_OPTION) {
                tblTags.removeRow(tblTags.getSelectedRows()[0]);
                tblTags.repaint();
                tblTags.dispatchEvent(new CellValueChangedEvent(tblTags, tblTags.getSelectedRows()[0], 0));
            }
        }
    }

    private void tableChanged() {
        HashMap<String, String> tags = ((Event) list.getSelectedValue()).getTags();
        for (int i = 0; i < tblTags.getRowCount(); i++) {
            tags.put("" + tblTags.getCellValue(i, 0), "" + tblTags.getCellValue(i, 1));
        }
    }

    private void generateScriptFile() {
        Project project = Project.getInstance();

        HashMap<String, ScriptObject> scriptMap = project.getDatabase().getScriptMap(Event.TYPE);
        if (scriptMap != null) {
            for (ScriptObject o : scriptMap.values()) {
                Event e = (Event) o;

                String scrpt = "-- OpenRPGStudio Event Script --\n"
                        + "-- Script is generated by application! Change with care! --\n\n"
                        + o.toScript() + "\n"
                        + "event.update = function()\n"
                        + updateMap.get(e) + "\n"
                        + "end\n"
                        + "event.action = function (caller)\n"
                        + actionMap.get(e) + "\n"
                        + "end\n\n"
                        + "Events.newEvent(event)";

                if (!project.getScriptRegister().isScriptRegistered("event_" + e.getID())) {
                    project.getScriptRegister().registerScript("event_" + e.getID(), Paths.get("event_" + e.getID() + ".lua"), 0);
                }

                ScriptTools.getInstance().saveScriptFile(project.getFolder("scripts").resolve("event_" + e.getID() + ".lua"), scrpt);
            }
        }
    }
}
